//
// Created by ole on 24-8-17.
//

#ifndef LOG_H
#define LOG_H
#include <iomanip>
#include <iostream>
#include <ostream>
#include <sstream>
#include <unistd.h>
#include <mutex>

#include "Utils.h"

class Log {
public:
#define NOISE() Log::print("N", __FUNCTION__, "Line "+std::to_string(__LINE__))

    static void e(const std::string &TAG, const std::string &msg) {
        print("E", TAG, msg);
    }

    static void w(const std::string &TAG, const std::string &msg) {
        print("W", TAG, msg);
    }

    static void i(const std::string &TAG, const std::string &msg) {
        print("I", TAG, msg);
    }

    static void d(const std::string &TAG, const std::string &msg) {
        print("D", TAG, msg);
    }

    static void print(const std::string &level, const std::string &TAG, const std::string &msg) {
        std::lock_guard<std::mutex> lock(mutex);
        std::cout << getCurrentTimeAsString() << getPidTid() << processTag(TAG) << " [" << level << "]:"
                << msg << std::endl;
    }

    static std::string getPidTid() {
        std::stringstream ss;
        ss << " (" << getpid() << "|" << Utils::getThreadId() << ")";
        return ss.str();
    }

    static std::string processTag(const std::string &tag) {
        std::string processedTag = tag;
        constexpr size_t maxLength = 14;

        if (processedTag.length() > maxLength) {
            constexpr size_t half = maxLength / 2;
            constexpr size_t start = half - 1;
            const size_t end = processedTag.length() - start - start + 1;
            processedTag.replace(start, end, "...");
        } else if (processedTag.length() < maxLength) {
            const size_t padding = maxLength - tag.length();
            const size_t leftPadding = padding / 2;
            const size_t rightPadding = padding - leftPadding;
            processedTag = std::string(leftPadding, ' ') + tag + std::string(rightPadding, ' ');
        }
        std::stringstream ss;
        ss << " <" << processedTag << ">";
        return ss.str();
    }

    static std::string getCurrentTimeAsString() {
        const auto now = std::chrono::system_clock::now();
        const auto now_as_time_t = std::chrono::system_clock::to_time_t(now);
        const auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
                                now.time_since_epoch()) % 1000;

        std::stringstream ss;
        ss << std::put_time(std::localtime(&now_as_time_t), "%Y-%m-%d %H:%M:%S")
                << '.' << std::setfill('0') << std::setw(3) << now_ms.count();
        return ss.str();
    }

private:
    static std::mutex mutex;
};

#endif //LOG_H
